Instalation directions.
Before installing the R packages insure the Java JDK is installed. You can download and install thj JDK from here
Before you start you must have installed the following R packages shown in the list below. We recommend that you follow the order shown here:
- install.packages(“devtools”)
- install.packages(“tidyverse”)
- install.packages(“nycflights13”)
- install.packages(“sparklyr”) # Use version 0.5.1
- install.packages(“digest”)
- install.packages(‘scales’)
- install.packages(‘prettyunits’)
- devtools::install_github(“hafen/trelliscopejs”)
- install.packages(‘httpuv’)
- install.packages(‘xtable’)
- library(sparklyr) # Load sparklyr before you install Spark
- spark_install(version = “1.6.2”)
Overview
In this tutorial we will explore methods for exploration and visualization of large complex data set using R and Spark. We will cover the following topics:
- Developing skills for exploring data in an iterative fashion. Since it is imposible to predict which views and summary of the data are the most interesting, an iterative process is required.
- Using the divide and recombine method on massive complex data set to compute summary statistics or prepare for visualization.
- Working Spark as a scalable back-end for divide and recombine.
- Plotting complex data, espcially using the the method of small fractions or conditining.
Introduction to divide and recombine
The divide and recombine or D&R method provides a highly scaleable approach to analysis of large complex data sets. With D&R we work with meaningful, persistent divisions of the data. “Big data” is typically big because it is made up of collections of many subsets, sensors, locations, time periods, etc. A schematic view of the D&R process is shown in the figure below.
There are many possible ways to divide data. The best choice depends on the nature of the data and the analysis to be performed. Some possibilities include:
- Break the data up based on data structure and apply visual or analytical methods
- We call this conditioning variable division
- In practice this approach is common and not new
- Another option is random replicate division
Once the data are divided, analytic or visual methods are applied independently to each subset in an embarrassingly parallel fashion. The results of these analysis are recombined to yield a statistically valid D&R result or visualization. We refer to these options as:
- Analytic recombination
- Summary and aggregation recombination
- Graphical recombination
In this lesson, our focus is on summary and graphical recombination for the exploration of large complex data sets.
Download the data file
Before running the rest of this notebook you must download the data file. We recommend that you only download these data once and save them in the working directory you are planning to use. This will allow you to start and run the network more quickly. Follow these steps:
- Set the
data_path variable to your working directory
- Run the code in this cell to download and save the data file.
# data_path <- "SET YOUR FILE Path HERE"
# File = "SOME FILE NAME"
# Read this with Spark?????
Large Data with R and Spark
This tutorial focuses on the exploration and visualization of large complex data sets using the D&R paradigm. To do so, we need a massively scalable back-end to perform the large scale data operations. In this case we are using a Spark back-end. The architecture our environment is shown schematically in the figure below.
The components of the architecture are:
- Spark back-end performs the large scale divide and recombine operatons.
- Spark can be run locally as we do in this tutorial or on a massive cluster. A Hadoop cluster or some other scalble back end can be used used.
- The d&R operations are performed within a Spark transform pipeline in the Spark session.
- Spark uses highly scalable storage options, such as HDFS.
- Sparklyr provides session management and transform orchestration for Spark.
- A local R session runing sparklyr and any other packages required controls the environment. The sparklyr translates the data munging pipeline defined in R into a transformation pipeline in Spark.
Starting and Connecting to Spark Cluster
With the R packages and Spark installed, its time to start a Spark cluster and create a connection with sparklyr. In this case, you will start Spark on your local machine. For large scale applications, Spark is run on a remove cluster.
The connection object, called sc in this case is the connetion between your local R session and Spark. You will use references to the Spark connection whenever you send data and commands to Spark or receive results back.
library(nycflights13)
library(tidyverse)
library(forcats)
library(sparklyr)
library(trelliscopejs)
sc <- spark_connect(master = "local")
Loading Data into Spark
Now that you have a Spark instance running, you can load the data from the .csv file in your local directory into Spark. If you are working with large scale data, you will need to use the more scalable data loading capabilities of Spark and will not load the data from a .csv file.
In no case can you load your large data set into your local R session. The point of the D&R paradigm is to use a massively scalable back end for the heavy lifting. Only the recombined results are collected into the local R session. In this case, we are using Spark for our backend. Other choices, such as Hadoop, would be suitable as well.
Notice, that the first argument of the command below is sc, a reference to the Spark connection you have started. The name assigned, flights_tbl is a reference you will use in R to access the data in Spark. Execute this code to load the data into your Spark session.
flights_tbl <- spark_read_csv(sc, "flights_csv", data_path)
A D&D Example: Exploring Data Using dplyr
Now that the data has been loaded into Spark we can start our first divide and recombine (D&R) example. The steps of this D&R example are:
- The data are divided by the airline code using a
group_by operation. In this case this procecss gives us 20 groups.
- The mean for each group is computed using the dplyr
summarize verb. These calculations are independent of each other in all respects. They can be done in parallel even on different nodes of a cluster. Any other summary statistics can be computed in parallel as well.
- The results are now just a mean value for each airline. They are easily recombined into a vector and then sorted using the
arrange verb.
Ideally we would have liked to compute quartiles and the median but sparklyr doesn’t support these calculations. Of course, you always have the option to do these caluclations with several primative steps.
The code below, applies a chain of dplyr verbs to the flights_tbl data frame. These operations are performed in Spark and transfered to your local R session using the collect verb. Execute this code and examine the result.
cr_arr_delay <- flights_tbl %>%
group_by(carrier) %>%
summarise(
mean_delay = mean(arr_delay),
mean_distance = mean(distance),
n = n()) %>%
arrange(mean_delay) %>%
collect()
cr_arr_delay # Print the results
The D&R process has reduced about 2 million rows of raw data to just 20 rows of summary statistics.
For this example, we used the dplyr package with sparklyr. The R dplyr package, combined with sparklyr, is used to script complex data munging and analysis operations in Spark.
- Dplyr performs common data manipulation or data munging operations using a series of operators call
verrbs.
- Complex data munging operations are constructed by chaining the simple verbs. The output of one verb is connected to the input of the next using the chaining operator,
%>%
- If you are not familar with dplyr there is a good tutorial vignette on CRAN.
- Sparklyr uses a subset of the dplyr verbs to script operation in Spark.
- Verb chains are defined in R.
- The pipeline defined by the verb chain is exectued in Spark.
- Results are uploaded to the local R session using the
collect verb.
- There are comprehensive tutorials an documentation for sparklyr.
Creating a First Plot
Now that you have the collected the summary statistics into your R session it is time to createt some plots to further explore the relationships in these results.
As a first step, we need to join some human readable names to the summary statistics data frame. Somem of these names are missing, so we will substitute the airline code in these cases.
# merge the airline info so we know who the carriers are
cr_arr_delay <- left_join(cr_arr_delay, airlines)
Joining, by = "carrier"
cr_arr_delay$name <- ifelse(is.na(cr_arr_delay$name), cr_arr_delay$carrier, cr_arr_delay$name)
cr_arr_delay
Now that the data set is prepared, let’s make some simple plots using the ggplot2 package. The code in cell below uses ggplot to explore the mean delay by airline name and the number of flights by airline.
ggplot(cr_arr_delay, aes(fct_reorder(name, mean_delay), mean_delay)) +
geom_point() +
# geom_bar()
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
xlab(NULL) + ylab("Mean Arrival Delay (minutes)")

ggplot(cr_arr_delay, aes(fct_reorder(name, n), n)) +
geom_point() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
xlab(NULL) + ylab("Total Flights")

Note: In this tutorial we assume you have some exposurer to the ggplot2 package.
- The
ggplotfunction defines a data frame to operate on.
- The
aes function defines the columns to use for the various dimensions of the plot, e.g. x, ycolor, shape.
- The plot type attribute is defined by one or more geometry functions, e.g. geom_point, geom_line, geom_boxplot.
- Other plot attributes are defined by the apprpriate function, e.g. xlab, ggtitle, theme.
- All of the functions required to create a complete plot are chained together with the chaining operator,
+. ***
Now, the question is, what is the relationship between number of flights and mean delay, and mean delay and mean distance of the flights. The code in the cells below displays these plots.
ggplot(cr_arr_delay, aes(mean_delay, n)) +
geom_point() +
# geom_bar()
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
xlab('Mean delay in minutes') + ylab("Number of flights by airline")

ggplot(cr_arr_delay, aes(mean_delay, mean_distance)) +
geom_point() +
# geom_bar()
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
xlab('Mean delay in minutes') + ylab("Mean distance in miles")

Plotting Complex Data
Let’s try another D&R example. In this case we will divide the data both by airline and month. The basic D&R pipeline is similar to the one we used before, but the results are more granular. The code in the cell below performes the following devide and recombine operations:
- The data is divided by each carrier and month pair.
- Summary statistics are computed for each division of the data.
- The recomnined results are collected to the local R session.
- The airlines names are joined and the airline codes are substituted for the missing values.
cr_mn_arr_delay <- flights_tbl %>%
group_by(carrier, month) %>%
summarise(
mean_delay = mean(arr_delay),
mean_distance = mean(distance),
n = n()) %>%
collect() %>%
left_join(airlines)
Joining, by = "carrier"
cr_mn_arr_delay$name <- ifelse(is.na(cr_mn_arr_delay$name), cr_mn_arr_delay$carrier, cr_mn_arr_delay$name)
head(cr_mn_arr_delay, 10)
There are a lot of values here, so we need a way to visualize this complex result. In this case we will use a powerful mathod know as a facet plot, conditioned plot, or trellis plot.
A faceted or conditioned plot is comprised of a set of sub-plots defined by one or more conditioning variables. The data in each sub-plot are sub-setted based on the values of the conditioning variable. This conditioning operation is, in effect, a group-by opertion. This approach allows small multiples of a large complex data set to be viewed in a systematic and understandable manner.
The idea of a facet plot has a long history. An early example of using small multiples was used to display some results from thee 1870 US census. The plot below combines small multiples with a treemap plot to show proportions of the population in ocupations or attending school,
The small multiples idea was popularized in Edward Tufte’s 1983 book. Bill Cleaveland and coleagues at AT&T Bell Labs created the Trellis Plot sofewware package based on the S lanuage. Cleaveland called this method trellis graphics.
The ggplot2 package contains the facet_grid function which is used to define the grid on which the sub-plots are created. The facet grid function uses an R formula object to define the rows and columns to specify the conditioning variable used to define the rows and columns. The general form of this formula is:
\[RowVariables \sim ColumnVariables\]
A conditioned plot with a single column, but multiple rows, is therefore defined:
\[RowVariables \sim\ .\]
Or, conditioned plot with a single row, but multiple columns, is defined:
\[.\ \sim ColumnVariables\]
You can use multiple variables to condition rows and columns, using the \(+\) symbol as the operator:
\[RowVar1 + RowVar2 + \ldots \sim ColVar1 + ColVar2 + \ldots\]
Like all good things in visulation, there are practical limits. Creating a large grid of sub-plots using multiple conditioning variables quickly becomes confussing to look at.
The code in the cell below creates a faceted plot of monthly average flight delay by month. The each each of the plots is grouped-by or conditioned on first the name of the airline and then the mean flight delay.
ggplot(cr_mn_arr_delay, aes(month, mean_delay)) +
geom_point() + geom_line() +
facet_grid(~ fct_reorder(name, mean_delay))

There is one plot for each airline, with the mean delay shown by month. These plots have been sorted by the mean delay by airline, so we can focus on the airlines with the greatest average delays. There is significant changes in the mean dealys by month for each airline.
Next, let’s look at the relationship between the airines and the number of flights. The code below creates a display of the number of flights per month by airline sorted by mean flight delay. The number of flights in a given month is displayed on a log scale.
# look at number of flights
ggplot(cr_mn_arr_delay, aes(month, n)) +
geom_point() + geom_line() +
scale_y_log10() +
facet_grid(~ fct_reorder(name, mean_delay))

Once againn, there is no clear pattern between the number of flights and the mean delays. Futher, for each airline there is only minimal changes in the number of flights by month.
Lets look at the same lot on a linear scale.
ggplot(cr_mn_arr_delay, aes(month, n)) +
geom_point() + geom_line() +
facet_grid(~ fct_reorder(name, mean_delay))

The conclusions we can draw from this chart are the same as before.
Filtering Results to Improve Understanding
To understand the month to month changes in mean flight delay for airlines we will plot these delays by month. To do so we need to filter the number of airlines to just the few with the largest number of flights. The code in the cell below does the following:
- Find the airline with the largest number of flights, and convert the airline codes to character.
- The pipeline for plotting the monthly flight delays does the following:
- The airlines are filtered for the ones with the large number of flights.
- A plot is created of the mean flight delay by month for the airlines with the largest numbers of flights.
top7 = cr_arr_delay %>% filter(n > 380000) %>% select(carrier)
top7 = sapply(top7, as.character)
top7
carrier
[1,] "US"
[2,] "WN"
[3,] "OO"
[4,] "DL"
[5,] "MQ"
[6,] "UA"
[7,] "AA"
# overlay them all
cr_mn_arr_delay %>%
filter(carrier %in% top7) %>%
ggplot(aes(month, mean_delay, color = name)) +
geom_point() + geom_line()

There is clearly a seasonal patern to the mean delays, which is similar for each airline.
Further Drill Down
Given this monthly pattern, it will be interesting to drill down into more detail. questions: - Are different destinations more prone to delays? - does variability across airlines change for different destinations?
let’s look into these by grouping by dest, month, and name we’ll look at mean delay for those airlines with enough observations. In this case we need to create a new grouping of the large data set using Sparlyr. The sparklyr pipeline in the cell below performs the following opertions:
- Groups the data first by the flight origin, then by the flight destinatiion and finally by month.
- The mean delay and number of flights on each route are computed.
- Results with fewer than 50 flights per month are filtered out.
- The results are collected back into your local R session.
# group by, origin, dest, carrier, month and get mean delay and # obs
# and pull this back into R
route_summ = flights_tbl %>%
group_by(origin, dest, carrier, month) %>%
summarise(
mean_delay = mean(arr_delay),
n = n()) %>%
filter(n >= 50) %>%
collect()
nrow(route_summ)
[1] 51623
We have gone from over 2 million rows to about 50 thousand rows, a reduction in side of about a factor of about 40. With some further filtering, we can reduce this event further. This type of reduction in data size is essential when applying the D&R paradigm to large complex data sets.
Let’s do some additional filtering to prepare these data for visualization. Our goal is to reduce the size of the data set an filter for the carriers with the with the most flights.
- We filter for the 7 airlines with the largest numbers of flights.
- The airline names are joined.
- The airline names are converted to a factor variable which is helpful for making plots with ggplot 2.
route_summ7 <-
filter(route_summ, carrier %in% top7) %>%
left_join(airlines) %>%
rename(carrier_name = name) %>%
mutate(carrier_name = factor(carrier_name))
Joining, by = "carrier"
# now let's nest the data by origin and dest (need to explain this...)
by_route <- route_summ7 %>%
group_by(origin, dest) %>%
nest()
by_route
We are left with about 2,200 routes. The data for each route is stored as a data frame in the ‘data’ column.
Note: If we had a large data set we could easily perform these transformations in our scalable back-end. ***
As a next step we filter some routes with sparse data. Specifically, we will filter for routes that have data for every month of the years. These steps are simple.
- We count the unique number of months.
- We calculate the unique number of carriers while we’re at it.
- Filter on the count of unique months.
by_route <- by_route %>%
mutate(
n_months = map_int(data, ~ n_distinct(.$month)),
n_carriers = map_int(data, ~ n_distinct(.$carrier))
# miny = map_dbl(data, ~ min(.$mean_delay, na.rm = TRUE)),
# maxy = map_dbl(data, ~ max(.$mean_delay, na.rm = TRUE))
)
by_route
This result is as expected. Now it is time to filter for routes with 12 months of data.
by_route <- filter(by_route, n_months == 12) %>%
select(-n_months)
by_route
There are only 1,700 routes with 12 months of data.
We can create a simple summary of these data by computing the mean delay by month
# let's summarize just by month
mn_arr_delay = flights_tbl %>%
group_by(month) %>%
summarise(mean_delay = mean(arr_delay)) %>%
arrange(month) %>%
collect()
mn_arr_delay
We have reduced our 2 million rows to just 10.
Ploting Complex Data with Trelliscope.
We have a complex data set with information on flight delays for 1,700 routes. How can we understand these complex data?
We have already looked at using the method of small fractions or conditioning to understand complex data. For this example, we will use a tool called Treliscope which allows interactive visualization of complex data sets using the method of small multiples.
# let's make a plot column for each route
by_route <- by_route %>%
mutate(
plot = map_plot(data, function(x) {
ggplot(x, aes(month, mean_delay, color = carrier_name)) +
geom_line(aes(month, mean_delay), data = mn_arr_delay,
color = "gray", size = 1) +
geom_point() + geom_line() +
ylim(c(-33.5, 96.25)) +
scale_color_discrete(drop = FALSE)
})
)
by_route
trelliscope(by_route, name = "test", nrow = 2, ncol = 4, self_contained = TRUE)
#browseURL(paste0(attr(p, "trelliscope_pars")$www_dir, "/index.html"))
LS0tDQp0aXRsZTogIkV4cGxvcmF0aW9uIGFuZCB2aXN1YWxpemF0aW9uIG9mIGxhcmdlLCBjb21wbGV4IGRhdGFzZXRzIHdpdGggUiwgSGFkb29wLCBhbmQgU3BhcmsiDQphdXRob3I6IFN0ZXBoZW4gRWxzdG9uIGFuZCBSeWFuIEhhZmVuDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBJbnN0YWxhdGlvbiBkaXJlY3Rpb25zLiANCg0KQmVmb3JlIGluc3RhbGxpbmcgdGhlIFIgcGFja2FnZXMgaW5zdXJlIHRoZSBKYXZhIEpESyBpcyBpbnN0YWxsZWQuIFlvdSBjYW4gZG93bmxvYWQgYW5kIGluc3RhbGwgdGhqIEpESyBmcm9tIFtoZXJlXShodHRwOi8vd3d3Lm9yYWNsZS5jb20vdGVjaG5ldHdvcmsvamF2YS9qYXZhc2UvZG93bmxvYWRzL2pkazgtZG93bmxvYWRzLTIxMzMxNTEuaHRtbCkgIA0KDQpCZWZvcmUgeW91IHN0YXJ0IHlvdSBtdXN0IGhhdmUgaW5zdGFsbGVkIHRoZSBmb2xsb3dpbmcgUiBwYWNrYWdlcyBzaG93biBpbiB0aGUgbGlzdCBiZWxvdy4gV2UgcmVjb21tZW5kIHRoYXQgeW91IGZvbGxvdyB0aGUgb3JkZXIgc2hvd24gaGVyZTogIA0KICANCi0gaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKSAgDQotIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpICAgIA0KLSBpbnN0YWxsLnBhY2thZ2VzKCJueWNmbGlnaHRzMTMiKSAgDQotICBpbnN0YWxsLnBhY2thZ2VzKCJzcGFya2x5ciIpICMgVXNlIHZlcnNpb24gMC41LjEgIA0KLSBpbnN0YWxsLnBhY2thZ2VzKCJkaWdlc3QiKSAgDQotIGluc3RhbGwucGFja2FnZXMoJ3NjYWxlcycpICANCi0gaW5zdGFsbC5wYWNrYWdlcygncHJldHR5dW5pdHMnKSAgDQotIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiaGFmZW4vdHJlbGxpc2NvcGVqcyIpICANCi0gaW5zdGFsbC5wYWNrYWdlcygnaHR0cHV2JykgIA0KLSBpbnN0YWxsLnBhY2thZ2VzKCd4dGFibGUnKSAgDQotIGxpYnJhcnkoc3BhcmtseXIpICMgTG9hZCBzcGFya2x5ciBiZWZvcmUgeW91IGluc3RhbGwgU3BhcmsgIA0KLSBzcGFya19pbnN0YWxsKHZlcnNpb24gPSAiMS42LjIiKSAgDQogDQojIyBPdmVydmlldw0KDQpJbiB0aGlzIHR1dG9yaWFsIHdlIHdpbGwgZXhwbG9yZSBtZXRob2RzIGZvciBleHBsb3JhdGlvbiBhbmQgIHZpc3VhbGl6YXRpb24gb2YgbGFyZ2UgY29tcGxleCBkYXRhIHNldCB1c2luZyBSIGFuZCBTcGFyay4gV2Ugd2lsbCBjb3ZlciB0aGUgZm9sbG93aW5nIHRvcGljczoNCg0KDQotIERldmVsb3Bpbmcgc2tpbGxzIGZvciBleHBsb3JpbmcgZGF0YSBpbiBhbiBpdGVyYXRpdmUgZmFzaGlvbi4gU2luY2UgaXQgaXMgaW1wb3NpYmxlIHRvIHByZWRpY3Qgd2hpY2ggdmlld3MgYW5kIHN1bW1hcnkgb2YgdGhlIGRhdGEgYXJlIHRoZSBtb3N0IGludGVyZXN0aW5nLCBhbiBpdGVyYXRpdmUgcHJvY2VzcyBpcyByZXF1aXJlZC4gDQotIFVzaW5nIHRoZSAgZGl2aWRlIGFuZCByZWNvbWJpbmUgbWV0aG9kIG9uIG1hc3NpdmUgY29tcGxleCBkYXRhIHNldCB0byBjb21wdXRlIHN1bW1hcnkgc3RhdGlzdGljcyBvciBwcmVwYXJlIGZvciB2aXN1YWxpemF0aW9uLg0KLSBXb3JraW5nIFNwYXJrIGFzIGEgc2NhbGFibGUgYmFjay1lbmQgZm9yIGRpdmlkZSBhbmQgcmVjb21iaW5lLg0KLSBQbG90dGluZyBjb21wbGV4IGRhdGEsIGVzcGNpYWxseSB1c2luZyB0aGUgdGhlICBtZXRob2Qgb2Ygc21hbGwgZnJhY3Rpb25zIG9yIGNvbmRpdGluaW5nLiANCg0KIyMgSW50cm9kdWN0aW9uIHRvIGRpdmlkZSBhbmQgcmVjb21iaW5lDQoNClRoZSAqKmRpdmlkZSBhbmQgcmVjb21iaW5lKiogb3IgKipEJlIqKiBtZXRob2QgcHJvdmlkZXMgYSBoaWdobHkgc2NhbGVhYmxlIGFwcHJvYWNoIHRvIGFuYWx5c2lzIG9mIGxhcmdlIGNvbXBsZXggZGF0YSBzZXRzLiBXaXRoIEQmUiB3ZSB3b3JrIHdpdGggbWVhbmluZ2Z1bCwgcGVyc2lzdGVudCBkaXZpc2lvbnMgb2YgdGhlIGRhdGEuICJCaWcgZGF0YSIgaXMgdHlwaWNhbGx5IGJpZyBiZWNhdXNlIGl0IGlzIG1hZGUgdXAgb2YgY29sbGVjdGlvbnMgb2YgbWFueSBzdWJzZXRzLCBzZW5zb3JzLCBsb2NhdGlvbnMsIHRpbWUgcGVyaW9kcywgZXRjLiBBIHNjaGVtYXRpYyB2aWV3IG9mIHRoZSBEJlIgcHJvY2VzcyBpcyBzaG93biBpbiB0aGUgZmlndXJlIGJlbG93Lg0KDQoNCiFbYWx0IHRleHRdKGRyZGlhZ3JhbS5wbmcpDQoNClRoZXJlIGFyZSBtYW55IHBvc3NpYmxlIHdheXMgdG8gZGl2aWRlIGRhdGEuIFRoZSBiZXN0IGNob2ljZSBkZXBlbmRzIG9uIHRoZSBuYXR1cmUgb2YgdGhlIGRhdGEgYW5kIHRoZSBhbmFseXNpcyB0byBiZSBwZXJmb3JtZWQuIFNvbWUgcG9zc2liaWxpdGllcyBpbmNsdWRlOiANCg0KLSBCcmVhayB0aGUgZGF0YSB1cCBiYXNlZCBvbiBkYXRhIHN0cnVjdHVyZSBhbmQgYXBwbHkgdmlzdWFsIG9yIGFuYWx5dGljYWwgbWV0aG9kcyANCi0gV2UgY2FsbCB0aGlzIGNvbmRpdGlvbmluZyB2YXJpYWJsZSBkaXZpc2lvbg0KLSBJbiBwcmFjdGljZSB0aGlzIGFwcHJvYWNoIGlzIGNvbW1vbiBhbmQgbm90IG5ldw0KLSBBbm90aGVyIG9wdGlvbiBpcyByYW5kb20gcmVwbGljYXRlIGRpdmlzaW9uDQoNCk9uY2UgdGhlIGRhdGEgYXJlIGRpdmlkZWQsIGFuYWx5dGljIG9yIHZpc3VhbCBtZXRob2RzIGFyZSBhcHBsaWVkIGluZGVwZW5kZW50bHkgdG8gZWFjaCBzdWJzZXQgaW4gYW4gKiplbWJhcnJhc3NpbmdseSBwYXJhbGxlbCoqIGZhc2hpb24uIFRoZSByZXN1bHRzIG9mIHRoZXNlIGFuYWx5c2lzIGFyZSAqKnJlY29tYmluZWQqKiB0byB5aWVsZCBhIHN0YXRpc3RpY2FsbHkgdmFsaWQgRCZSIHJlc3VsdCBvciB2aXN1YWxpemF0aW9uLiBXZSByZWZlciB0byB0aGVzZSBvcHRpb25zIGFzOiANCg0KLSBBbmFseXRpYyByZWNvbWJpbmF0aW9uDQotIFN1bW1hcnkgYW5kIGFnZ3JlZ2F0aW9uIHJlY29tYmluYXRpb24NCi0gR3JhcGhpY2FsIHJlY29tYmluYXRpb24NCg0KSW4gdGhpcyBsZXNzb24sIG91ciBmb2N1cyBpcyBvbiBzdW1tYXJ5IGFuZCBncmFwaGljYWwgcmVjb21iaW5hdGlvbiBmb3IgdGhlIGV4cGxvcmF0aW9uIG9mIGxhcmdlIGNvbXBsZXggZGF0YSBzZXRzLiANCg0KIyMgRG93bmxvYWQgdGhlIGRhdGEgZmlsZSAgDQoNCkJlZm9yZSBydW5uaW5nIHRoZSByZXN0IG9mIHRoaXMgbm90ZWJvb2sgeW91IG11c3QgZG93bmxvYWQgdGhlIGRhdGEgZmlsZS4gV2UgcmVjb21tZW5kIHRoYXQgeW91IG9ubHkgZG93bmxvYWQgdGhlc2UgZGF0YSBvbmNlIGFuZCBzYXZlIHRoZW0gaW4gdGhlIHdvcmtpbmcgZGlyZWN0b3J5IHlvdSBhcmUgcGxhbm5pbmcgdG8gdXNlLiBUaGlzIHdpbGwgYWxsb3cgeW91IHRvIHN0YXJ0IGFuZCBydW4gdGhlIG5ldHdvcmsgbW9yZSBxdWlja2x5LiBGb2xsb3cgdGhlc2Ugc3RlcHM6ICANCg0KLSBTZXQgdGhlIGBkYXRhX3BhdGhgIHZhcmlhYmxlIHRvIHlvdXIgd29ya2luZyBkaXJlY3RvcnkgIA0KLSBSdW4gdGhlIGNvZGUgaW4gdGhpcyBjZWxsIHRvIGRvd25sb2FkIGFuZCBzYXZlIHRoZSBkYXRhIGZpbGUuIA0KDQpgYGB7cn0NCiMgZGF0YV9wYXRoIDwtICJTRVQgWU9VUiBGSUxFIFBhdGggSEVSRSINCiMgRmlsZSA9ICJTT01FIEZJTEUgTkFNRSINCiMgUmVhZCB0aGlzIHdpdGggU3Bhcms/Pz8/Pw0KYGBgDQoNCiMjIExhcmdlIERhdGEgd2l0aCBSIGFuZCBTcGFyaw0KDQpUaGlzIHR1dG9yaWFsIGZvY3VzZXMgb24gdGhlIGV4cGxvcmF0aW9uIGFuZCB2aXN1YWxpemF0aW9uIG9mIGxhcmdlIGNvbXBsZXggZGF0YSBzZXRzIHVzaW5nIHRoZSBEJlIgcGFyYWRpZ20uIFRvIGRvIHNvLCB3ZSBuZWVkIGEgbWFzc2l2ZWx5IHNjYWxhYmxlIGJhY2stZW5kIHRvIHBlcmZvcm0gdGhlIGxhcmdlIHNjYWxlIGRhdGEgb3BlcmF0aW9ucy4gSW4gdGhpcyBjYXNlIHdlIGFyZSB1c2luZyBhIFNwYXJrIGJhY2stZW5kLiBUaGUgYXJjaGl0ZWN0dXJlIG91ciBlbnZpcm9ubWVudCBpcyBzaG93biBzY2hlbWF0aWNhbGx5IGluIHRoZSBmaWd1cmUgYmVsb3cuIA0KDQohW10oU3BhcmtseXIuanBnKQ0KDQpUaGUgY29tcG9uZW50cyBvZiB0aGUgYXJjaGl0ZWN0dXJlIGFyZToNCg0KLSBTcGFyayBiYWNrLWVuZCBwZXJmb3JtcyB0aGUgbGFyZ2Ugc2NhbGUgZGl2aWRlIGFuZCByZWNvbWJpbmUgb3BlcmF0b25zLiANCiAgLSBTcGFyayBjYW4gYmUgcnVuIGxvY2FsbHkgYXMgd2UgZG8gaW4gdGhpcyB0dXRvcmlhbCBvciBvbiBhIG1hc3NpdmUgY2x1c3Rlci4gQSBIYWRvb3AgY2x1c3RlciBvciBzb21lIG90aGVyIHNjYWxibGUgYmFjayBlbmQgY2FuIGJlIHVzZWQgdXNlZC4gDQogIC0gVGhlIGQmUiBvcGVyYXRpb25zIGFyZSBwZXJmb3JtZWQgd2l0aGluIGEgU3BhcmsgdHJhbnNmb3JtIHBpcGVsaW5lIGluIHRoZSBTcGFyayBzZXNzaW9uLg0KICAtIFNwYXJrIHVzZXMgaGlnaGx5IHNjYWxhYmxlIHN0b3JhZ2Ugb3B0aW9ucywgc3VjaCBhcyBIREZTLg0KLSBTcGFya2x5ciBwcm92aWRlcyBzZXNzaW9uIG1hbmFnZW1lbnQgYW5kIHRyYW5zZm9ybSBvcmNoZXN0cmF0aW9uIGZvciBTcGFyay4gDQotIEEgbG9jYWwgUiBzZXNzaW9uIHJ1bmluZyBzcGFya2x5ciBhbmQgYW55IG90aGVyIHBhY2thZ2VzIHJlcXVpcmVkIGNvbnRyb2xzIHRoZSBlbnZpcm9ubWVudC4gVGhlIHNwYXJrbHlyIHRyYW5zbGF0ZXMgdGhlIGRhdGEgbXVuZ2luZyBwaXBlbGluZSAgZGVmaW5lZCBpbiBSIGludG8gYSB0cmFuc2Zvcm1hdGlvbiBwaXBlbGluZSBpbiBTcGFyay4gDQoNCg0KIyMjIFN0YXJ0aW5nIGFuZCBDb25uZWN0aW5nIHRvIFNwYXJrIENsdXN0ZXIgIA0KDQpXaXRoIHRoZSBSIHBhY2thZ2VzIGFuZCBTcGFyayBpbnN0YWxsZWQsIGl0cyB0aW1lIHRvIHN0YXJ0IGEgU3BhcmsgY2x1c3RlciBhbmQgY3JlYXRlIGEgY29ubmVjdGlvbiB3aXRoIGBzcGFya2x5cmAuIEluIHRoaXMgY2FzZSwgeW91IHdpbGwgc3RhcnQgU3Bhcmsgb24geW91ciBsb2NhbCBtYWNoaW5lLiBGb3IgbGFyZ2Ugc2NhbGUgYXBwbGljYXRpb25zLCBTcGFyayBpcyBydW4gb24gYSByZW1vdmUgY2x1c3Rlci4gIA0KDQpUaGUgY29ubmVjdGlvbiBvYmplY3QsIGNhbGxlZCBgc2MgaW4gdGhpcyBjYXNlYCBpcyB0aGUgY29ubmV0aW9uIGJldHdlZW4geW91ciBsb2NhbCBSIHNlc3Npb24gYW5kIFNwYXJrLiBZb3Ugd2lsbCB1c2UgcmVmZXJlbmNlcyB0byB0aGUgU3BhcmsgY29ubmVjdGlvbiB3aGVuZXZlciB5b3Ugc2VuZCBkYXRhIGFuZCBjb21tYW5kcyB0byBTcGFyayBvciByZWNlaXZlIHJlc3VsdHMgYmFjay4gIA0KDQpgYGB7Un0NCmxpYnJhcnkobnljZmxpZ2h0czEzKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGZvcmNhdHMpDQpsaWJyYXJ5KHNwYXJrbHlyKQ0KbGlicmFyeSh0cmVsbGlzY29wZWpzKQ0KDQpzYyA8LSBzcGFya19jb25uZWN0KG1hc3RlciA9ICJsb2NhbCIpDQpgYGANCg0KDQojIyMgTG9hZGluZyBEYXRhIGludG8gU3BhcmsNCg0KTm93IHRoYXQgeW91IGhhdmUgYSBTcGFyayBpbnN0YW5jZSBydW5uaW5nLCB5b3UgY2FuIGxvYWQgdGhlIGRhdGEgZnJvbSB0aGUgLmNzdiBmaWxlIGluIHlvdXIgbG9jYWwgZGlyZWN0b3J5IGludG8gU3BhcmsuIElmIHlvdSBhcmUgd29ya2luZyB3aXRoIGxhcmdlIHNjYWxlIGRhdGEsIHlvdSB3aWxsIG5lZWQgdG8gdXNlIHRoZSBtb3JlIHNjYWxhYmxlIGRhdGEgbG9hZGluZyBjYXBhYmlsaXRpZXMgb2YgU3BhcmsgYW5kIHdpbGwgbm90IGxvYWQgdGhlIGRhdGEgZnJvbSBhIC5jc3YgZmlsZS4gDQoNCkluIG5vIGNhc2UgY2FuIHlvdSBsb2FkIHlvdXIgbGFyZ2UgZGF0YSBzZXQgaW50byB5b3VyIGxvY2FsIFIgc2Vzc2lvbi4gVGhlIHBvaW50IG9mIHRoZSBEJlIgcGFyYWRpZ20gaXMgdG8gdXNlIGEgbWFzc2l2ZWx5IHNjYWxhYmxlIGJhY2sgZW5kIGZvciB0aGUgaGVhdnkgbGlmdGluZy4gT25seSB0aGUgcmVjb21iaW5lZCByZXN1bHRzIGFyZSBjb2xsZWN0ZWQgaW50byB0aGUgbG9jYWwgUiBzZXNzaW9uLiBJbiB0aGlzIGNhc2UsIHdlIGFyZSB1c2luZyBTcGFyayBmb3Igb3VyIGJhY2tlbmQuIE90aGVyIGNob2ljZXMsIHN1Y2ggYXMgSGFkb29wLCB3b3VsZCBiZSBzdWl0YWJsZSBhcyB3ZWxsLiANCg0KTm90aWNlLCB0aGF0IHRoZSBmaXJzdCBhcmd1bWVudCBvZiB0aGUgY29tbWFuZCBiZWxvdyBpcyBgc2NgLCBhIHJlZmVyZW5jZSB0byB0aGUgU3BhcmsgY29ubmVjdGlvbiB5b3UgaGF2ZSBzdGFydGVkLiBUaGUgbmFtZSBhc3NpZ25lZCwgYGZsaWdodHNfdGJsYCBpcyBhIHJlZmVyZW5jZSB5b3Ugd2lsbCB1c2UgaW4gUiB0byBhY2Nlc3MgdGhlIGRhdGEgaW4gU3BhcmsuIEV4ZWN1dGUgdGhpcyBjb2RlIHRvIGxvYWQgdGhlIGRhdGEgaW50byB5b3VyIFNwYXJrIHNlc3Npb24uICANCg0KYGBge1J9DQpmbGlnaHRzX3RibCA8LSBzcGFya19yZWFkX2NzdihzYywgImZsaWdodHNfY3N2IiwgZGF0YV9wYXRoKQ0KYGBgDQoNCg0KIyMgQSBEJkQgRXhhbXBsZTogRXhwbG9yaW5nIERhdGEgVXNpbmcgZHBseXIgDQoNCk5vdyB0aGF0IHRoZSBkYXRhIGhhcyBiZWVuIGxvYWRlZCBpbnRvIFNwYXJrIHdlIGNhbiBzdGFydCBvdXIgZmlyc3QgKipkaXZpZGUgYW5kIHJlY29tYmluZSAoRCZSKSoqIGV4YW1wbGUuIFRoZSBzdGVwcyBvZiB0aGlzIEQmUiBleGFtcGxlIGFyZToNCg0KLSBUaGUgZGF0YSBhcmUgZGl2aWRlZCBieSB0aGUgYWlybGluZSBjb2RlIHVzaW5nIGEgYGdyb3VwX2J5YCBvcGVyYXRpb24uIEluIHRoaXMgY2FzZSB0aGlzIHByb2NlY3NzIGdpdmVzIHVzIDIwIGdyb3Vwcy4gDQotIFRoZSBtZWFuIGZvciBlYWNoIGdyb3VwIGlzIGNvbXB1dGVkIHVzaW5nIHRoZSBkcGx5ciBgc3VtbWFyaXplYCB2ZXJiLiBUaGVzZSBjYWxjdWxhdGlvbnMgYXJlIGluZGVwZW5kZW50IG9mIGVhY2ggb3RoZXIgaW4gYWxsIHJlc3BlY3RzLiBUaGV5IGNhbiBiZSBkb25lIGluIHBhcmFsbGVsIGV2ZW4gb24gZGlmZmVyZW50IG5vZGVzIG9mIGEgY2x1c3Rlci4gQW55IG90aGVyIHN1bW1hcnkgc3RhdGlzdGljcyBjYW4gYmUgY29tcHV0ZWQgaW4gcGFyYWxsZWwgYXMgd2VsbC4NCi0gVGhlIHJlc3VsdHMgYXJlIG5vdyBqdXN0IGEgbWVhbiB2YWx1ZSBmb3IgZWFjaCBhaXJsaW5lLiBUaGV5IGFyZSBlYXNpbHkgcmVjb21iaW5lZCBpbnRvIGEgdmVjdG9yIGFuZCB0aGVuIHNvcnRlZCB1c2luZyB0aGUgYGFycmFuZ2VgIHZlcmIuIA0KDQpJZGVhbGx5IHdlIHdvdWxkIGhhdmUgbGlrZWQgdG8gY29tcHV0ZSBxdWFydGlsZXMgYW5kIHRoZSBtZWRpYW4gYnV0IHNwYXJrbHlyIGRvZXNuJ3Qgc3VwcG9ydCB0aGVzZSBjYWxjdWxhdGlvbnMuIE9mIGNvdXJzZSwgeW91IGFsd2F5cyBoYXZlIHRoZSBvcHRpb24gdG8gZG8gdGhlc2UgY2FsdWNsYXRpb25zIHdpdGggc2V2ZXJhbCBwcmltYXRpdmUgc3RlcHMuIA0KDQoNClRoZSBjb2RlIGJlbG93LCBhcHBsaWVzIGEgY2hhaW4gb2YgZHBseXIgKip2ZXJicyoqIHRvIHRoZSBgZmxpZ2h0c190YmxgIGRhdGEgZnJhbWUuIFRoZXNlIG9wZXJhdGlvbnMgYXJlIHBlcmZvcm1lZCBpbiBTcGFyayBhbmQgdHJhbnNmZXJlZCB0byB5b3VyIGxvY2FsIFIgc2Vzc2lvbiB1c2luZyB0aGUgYGNvbGxlY3RgIHZlcmIuIEV4ZWN1dGUgdGhpcyBjb2RlIGFuZCBleGFtaW5lIHRoZSByZXN1bHQuICANCg0KYGBge1J9DQpjcl9hcnJfZGVsYXkgPC0gZmxpZ2h0c190YmwgJT4lDQogIGdyb3VwX2J5KGNhcnJpZXIpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbWVhbl9kZWxheSA9IG1lYW4oYXJyX2RlbGF5KSwNCiAgICBtZWFuX2Rpc3RhbmNlID0gbWVhbihkaXN0YW5jZSksDQogICAgbiA9IG4oKSkgJT4lDQogIGFycmFuZ2UobWVhbl9kZWxheSkgJT4lDQogIGNvbGxlY3QoKQ0KDQpjcl9hcnJfZGVsYXkgIyBQcmludCB0aGUgcmVzdWx0cw0KYGBgDQoNClRoZSBEJlIgcHJvY2VzcyBoYXMgcmVkdWNlZCBhYm91dCAyIG1pbGxpb24gcm93cyBvZiByYXcgZGF0YSB0byBqdXN0IDIwIHJvd3Mgb2Ygc3VtbWFyeSBzdGF0aXN0aWNzLiANCg0KRm9yIHRoaXMgZXhhbXBsZSwgd2UgdXNlZCB0aGUgZHBseXIgcGFja2FnZSB3aXRoIHNwYXJrbHlyLiBUaGUgUiBkcGx5ciBwYWNrYWdlLCBjb21iaW5lZCB3aXRoIHNwYXJrbHlyLCBpcyB1c2VkIHRvIHNjcmlwdCBjb21wbGV4IGRhdGEgbXVuZ2luZyBhbmQgYW5hbHlzaXMgb3BlcmF0aW9ucyBpbiBTcGFyay4gDQoNCi0gRHBseXIgcGVyZm9ybXMgY29tbW9uIGRhdGEgbWFuaXB1bGF0aW9uIG9yIGRhdGEgbXVuZ2luZyBvcGVyYXRpb25zIHVzaW5nIGEgc2VyaWVzIG9mIG9wZXJhdG9ycyBjYWxsIGB2ZXJyYnNgLiANCiAgLSBDb21wbGV4IGRhdGEgbXVuZ2luZyBvcGVyYXRpb25zIGFyZSBjb25zdHJ1Y3RlZCBieSAqKmNoYWluaW5nKiogdGhlIHNpbXBsZSB2ZXJicy4gVGhlIG91dHB1dCBvZiBvbmUgdmVyYiBpcyBjb25uZWN0ZWQgdG8gdGhlIGlucHV0IG9mIHRoZSBuZXh0IHVzaW5nIHRoZSAqKmNoYWluaW5nIG9wZXJhdG9yKiosIGAlPiVgDQogIC0gSWYgeW91IGFyZSBub3QgZmFtaWxhciB3aXRoIGRwbHlyIHRoZXJlIGlzIGEgZ29vZCBbdHV0b3JpYWwgdmlnbmV0dGVdKGh0dHBzOi8vY3Jhbi5yc3R1ZGlvLmNvbS93ZWIvcGFja2FnZXMvZHBseXIvdmlnbmV0dGVzL2ludHJvZHVjdGlvbi5odG1sKSBvbiBDUkFOLiAgDQotIFNwYXJrbHlyIHVzZXMgYSBzdWJzZXQgb2YgdGhlIGRwbHlyIHZlcmJzIHRvIHNjcmlwdCBvcGVyYXRpb24gaW4gU3BhcmsuIA0KICAtIFZlcmIgY2hhaW5zIGFyZSBkZWZpbmVkIGluIFIuDQogIC0gVGhlIHBpcGVsaW5lIGRlZmluZWQgYnkgdGhlIHZlcmIgY2hhaW4gaXMgZXhlY3R1ZWQgaW4gU3BhcmsuDQogIC0gUmVzdWx0cyBhcmUgdXBsb2FkZWQgdG8gdGhlIGxvY2FsIFIgc2Vzc2lvbiB1c2luZyB0aGUgYGNvbGxlY3RgIHZlcmIuDQogIC0gVGhlcmUgYXJlIGNvbXByZWhlbnNpdmUgW3R1dG9yaWFscyBhbiBkb2N1bWVudGF0aW9uXShodHRwOi8vc3BhcmsucnN0dWRpby5jb20vKSBmb3Igc3BhcmtseXIuDQoNCg0KIyMgQ3JlYXRpbmcgYSBGaXJzdCBQbG90DQoNCk5vdyB0aGF0IHlvdSBoYXZlIHRoZSBjb2xsZWN0ZWQgdGhlIHN1bW1hcnkgc3RhdGlzdGljcyBpbnRvIHlvdXIgUiBzZXNzaW9uIGl0IGlzIHRpbWUgdG8gY3JlYXRldCBzb21lIHBsb3RzIHRvIGZ1cnRoZXIgZXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwcyBpbiB0aGVzZSByZXN1bHRzLiANCg0KQXMgYSBmaXJzdCBzdGVwLCB3ZSBuZWVkIHRvIGpvaW4gc29tZSBodW1hbiByZWFkYWJsZSBuYW1lcyB0byB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIGRhdGEgZnJhbWUuIFNvbWVtIG9mIHRoZXNlIG5hbWVzIGFyZSBtaXNzaW5nLCBzbyB3ZSB3aWxsIHN1YnN0aXR1dGUgdGhlIGFpcmxpbmUgY29kZSBpbiB0aGVzZSBjYXNlcy4gDQoNCmBgYHtSfQ0KIyBtZXJnZSB0aGUgYWlybGluZSBpbmZvIHNvIHdlIGtub3cgd2hvIHRoZSBjYXJyaWVycyBhcmUNCmNyX2Fycl9kZWxheSA8LSBsZWZ0X2pvaW4oY3JfYXJyX2RlbGF5LCBhaXJsaW5lcykNCmNyX2Fycl9kZWxheSRuYW1lIDwtIGlmZWxzZShpcy5uYShjcl9hcnJfZGVsYXkkbmFtZSksIGNyX2Fycl9kZWxheSRjYXJyaWVyLCBjcl9hcnJfZGVsYXkkbmFtZSkNCg0KY3JfYXJyX2RlbGF5DQpgYGANCg0KTm93IHRoYXQgdGhlIGRhdGEgc2V0IGlzIHByZXBhcmVkLCBsZXQncyBtYWtlIHNvbWUgc2ltcGxlIHBsb3RzIHVzaW5nIHRoZSBgZ2dwbG90MmAgcGFja2FnZS4gVGhlIGNvZGUgaW4gY2VsbCBiZWxvdyB1c2VzIGdncGxvdCB0byBleHBsb3JlIHRoZSBtZWFuIGRlbGF5IGJ5IGFpcmxpbmUgbmFtZSBhbmQgdGhlIG51bWJlciBvZiBmbGlnaHRzIGJ5IGFpcmxpbmUuIA0KDQoNCmBgYHtSfQ0KZ2dwbG90KGNyX2Fycl9kZWxheSwgYWVzKGZjdF9yZW9yZGVyKG5hbWUsIG1lYW5fZGVsYXkpLCBtZWFuX2RlbGF5KSkgKw0KICBnZW9tX3BvaW50KCkgKw0KIyAgZ2VvbV9iYXIoKQ0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArDQogIHhsYWIoTlVMTCkgKyB5bGFiKCJNZWFuIEFycml2YWwgRGVsYXkgKG1pbnV0ZXMpIikNCg0KZ2dwbG90KGNyX2Fycl9kZWxheSwgYWVzKGZjdF9yZW9yZGVyKG5hbWUsIG4pLCBuKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArDQogIHhsYWIoTlVMTCkgKyB5bGFiKCJUb3RhbCBGbGlnaHRzIikNCg0KYGBgDQoNCg0KKioqDQoqKk5vdGU6KiogSW4gdGhpcyB0dXRvcmlhbCB3ZSBhc3N1bWUgeW91IGhhdmUgc29tZSBleHBvc3VyZXIgdG8gdGhlIGdncGxvdDIgcGFja2FnZS4gDQoNCi0gVGhlYGdncGxvdGBmdW5jdGlvbiBkZWZpbmVzIGEgZGF0YSBmcmFtZSB0byBvcGVyYXRlIG9uLg0KLSBUaGUgYGFlc2AgZnVuY3Rpb24gZGVmaW5lcyB0aGUgY29sdW1ucyB0byB1c2UgZm9yIHRoZSB2YXJpb3VzIGRpbWVuc2lvbnMgb2YgdGhlIHBsb3QsIGUuZy4geCwgeWNvbG9yLCBzaGFwZS4gDQotIFRoZSBwbG90IHR5cGUgYXR0cmlidXRlIGlzIGRlZmluZWQgYnkgb25lIG9yIG1vcmUgZ2VvbWV0cnkgZnVuY3Rpb25zLCBlLmcuIGdlb21fcG9pbnQsIGdlb21fbGluZSwgZ2VvbV9ib3hwbG90Lg0KLSBPdGhlciBwbG90IGF0dHJpYnV0ZXMgYXJlIGRlZmluZWQgYnkgdGhlIGFwcHJwcmlhdGUgZnVuY3Rpb24sIGUuZy4geGxhYiwgZ2d0aXRsZSwgdGhlbWUuIA0KLSBBbGwgb2YgdGhlIGZ1bmN0aW9ucyByZXF1aXJlZCB0byBjcmVhdGUgYSBjb21wbGV0ZSBwbG90IGFyZSAqKmNoYWluZWQqKiB0b2dldGhlciB3aXRoIHRoZSAqKmNoYWluaW5nKiogb3BlcmF0b3IsIGArYC4NCioqKg0KDQoNCk5vdywgdGhlIHF1ZXN0aW9uIGlzLCB3aGF0IGlzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBudW1iZXIgb2YgZmxpZ2h0cyBhbmQgbWVhbiBkZWxheSwgYW5kIG1lYW4gZGVsYXkgYW5kIG1lYW4gZGlzdGFuY2Ugb2YgdGhlIGZsaWdodHMuIFRoZSBjb2RlIGluIHRoZSBjZWxscyBiZWxvdyBkaXNwbGF5cyB0aGVzZSBwbG90cy4NCg0KYGBge1J9DQpnZ3Bsb3QoY3JfYXJyX2RlbGF5LCBhZXMobWVhbl9kZWxheSwgbikpICsNCiAgZ2VvbV9wb2ludCgpICsNCiMgIGdlb21fYmFyKCkNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKw0KICB4bGFiKCdNZWFuIGRlbGF5IGluIG1pbnV0ZXMnKSArIHlsYWIoIk51bWJlciBvZiBmbGlnaHRzIGJ5IGFpcmxpbmUiKQ0KDQpnZ3Bsb3QoY3JfYXJyX2RlbGF5LCBhZXMobWVhbl9kZWxheSwgbWVhbl9kaXN0YW5jZSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiMgIGdlb21fYmFyKCkNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKw0KICB4bGFiKCdNZWFuIGRlbGF5IGluIG1pbnV0ZXMnKSArIHlsYWIoIk1lYW4gZGlzdGFuY2UgaW4gbWlsZXMiKQ0KYGBgDQoNCg0KDQojIyBQbG90dGluZyAgQ29tcGxleCBEYXRhIA0KDQpMZXQncyB0cnkgYW5vdGhlciBEJlIgZXhhbXBsZS4gSW4gdGhpcyBjYXNlIHdlIHdpbGwgZGl2aWRlIHRoZSBkYXRhIGJvdGggYnkgYWlybGluZSBhbmQgbW9udGguIFRoZSBiYXNpYyBEJlIgcGlwZWxpbmUgaXMgc2ltaWxhciB0byB0aGUgb25lIHdlIHVzZWQgYmVmb3JlLCBidXQgdGhlIHJlc3VsdHMgYXJlIG1vcmUgZ3JhbnVsYXIuIFRoZSBjb2RlIGluIHRoZSBjZWxsIGJlbG93IHBlcmZvcm1lcyB0aGUgZm9sbG93aW5nIGRldmlkZSBhbmQgcmVjb21iaW5lIG9wZXJhdGlvbnM6DQoNCi0gVGhlIGRhdGEgaXMgZGl2aWRlZCBieSBlYWNoIGNhcnJpZXIgYW5kIG1vbnRoIHBhaXIuDQotIFN1bW1hcnkgc3RhdGlzdGljcyBhcmUgY29tcHV0ZWQgZm9yIGVhY2ggZGl2aXNpb24gb2YgdGhlIGRhdGEuDQotIFRoZSByZWNvbW5pbmVkIHJlc3VsdHMgYXJlIGNvbGxlY3RlZCB0byB0aGUgbG9jYWwgUiBzZXNzaW9uLg0KLSBUaGUgYWlybGluZXMgbmFtZXMgYXJlIGpvaW5lZCBhbmQgdGhlIGFpcmxpbmUgY29kZXMgYXJlIHN1YnN0aXR1dGVkIGZvciB0aGUgbWlzc2luZyB2YWx1ZXMuIA0KDQoNCmBgYHtSfQ0KY3JfbW5fYXJyX2RlbGF5IDwtIGZsaWdodHNfdGJsICU+JQ0KICBncm91cF9ieShjYXJyaWVyLCBtb250aCkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBtZWFuX2RlbGF5ID0gbWVhbihhcnJfZGVsYXkpLA0KICAgIG1lYW5fZGlzdGFuY2UgPSBtZWFuKGRpc3RhbmNlKSwNCiAgICBuID0gbigpKSAlPiUNCiAgY29sbGVjdCgpICU+JQ0KICBsZWZ0X2pvaW4oYWlybGluZXMpDQoNCmNyX21uX2Fycl9kZWxheSRuYW1lIDwtIGlmZWxzZShpcy5uYShjcl9tbl9hcnJfZGVsYXkkbmFtZSksIGNyX21uX2Fycl9kZWxheSRjYXJyaWVyLCBjcl9tbl9hcnJfZGVsYXkkbmFtZSkgIA0KDQpoZWFkKGNyX21uX2Fycl9kZWxheSwgMTApICANCmBgYA0KDQpUaGVyZSBhcmUgYSBsb3Qgb2YgdmFsdWVzIGhlcmUsIHNvIHdlIG5lZWQgYSB3YXkgdG8gdmlzdWFsaXplIHRoaXMgY29tcGxleCByZXN1bHQuIEluIHRoaXMgY2FzZSB3ZSB3aWxsIHVzZSBhIHBvd2VyZnVsIG1hdGhvZCBrbm93IGFzIGEgKipmYWNldCBwbG90KiosICoqY29uZGl0aW9uZWQgcGxvdCoqLCBvciAqKnRyZWxsaXMgcGxvdCoqLiANCg0KQSBmYWNldGVkIG9yIGNvbmRpdGlvbmVkIHBsb3QgaXMgY29tcHJpc2VkIG9mIGEgc2V0IG9mIHN1Yi1wbG90cyBkZWZpbmVkIGJ5IG9uZSBvciBtb3JlIGNvbmRpdGlvbmluZyB2YXJpYWJsZXMuIFRoZSBkYXRhIGluIGVhY2ggc3ViLXBsb3QgYXJlIHN1Yi1zZXR0ZWQgYmFzZWQgb24gdGhlIHZhbHVlcyBvZiB0aGUgY29uZGl0aW9uaW5nIHZhcmlhYmxlLiBUaGlzIGNvbmRpdGlvbmluZyBvcGVyYXRpb24gaXMsIGluIGVmZmVjdCwgYSAqKmdyb3VwLWJ5Kiogb3BlcnRpb24uIFRoaXMgYXBwcm9hY2ggYWxsb3dzICoqc21hbGwgbXVsdGlwbGVzKiogb2YgYSBsYXJnZSBjb21wbGV4IGRhdGEgc2V0IHRvIGJlIHZpZXdlZCBpbiBhIHN5c3RlbWF0aWMgYW5kIHVuZGVyc3RhbmRhYmxlIG1hbm5lci4NCg0KVGhlIGlkZWEgb2YgYSBmYWNldCBwbG90IGhhcyBhIGxvbmcgaGlzdG9yeS4gQW4gZWFybHkgZXhhbXBsZSBvZiB1c2luZyBzbWFsbCBtdWx0aXBsZXMgd2FzIHVzZWQgdG8gZGlzcGxheSBzb21lIHJlc3VsdHMgZnJvbSB0aGVlIDE4NzAgVVMgY2Vuc3VzLiBUaGUgcGxvdCBiZWxvdyBjb21iaW5lcyBzbWFsbCBtdWx0aXBsZXMgd2l0aCBhIHRyZWVtYXAgcGxvdCB0byBzaG93IHByb3BvcnRpb25zIG9mIHRoZSBwb3B1bGF0aW9uIGluIG9jdXBhdGlvbnMgb3IgYXR0ZW5kaW5nIHNjaG9vbCwgDQoNCiFbXShTbWFsbF9NdWx0aXBsZXNfMTg3MC5qcGcpDQoNClRoZSBzbWFsbCBtdWx0aXBsZXMgaWRlYSB3YXMgcG9wdWxhcml6ZWQgaW4gRWR3YXJkIFR1ZnRlJ3MgMTk4MyBib29rLiBCaWxsIENsZWF2ZWxhbmQgYW5kIGNvbGVhZ3VlcyBhdCBBVCZUIEJlbGwgTGFicyBjcmVhdGVkIHRoZSBUcmVsbGlzIFBsb3Qgc29mZXd3YXJlIHBhY2thZ2UgYmFzZWQgb24gdGhlIFMgbGFudWFnZS4gQ2xlYXZlbGFuZCBjYWxsZWQgdGhpcyBtZXRob2QgdHJlbGxpcyBncmFwaGljcy4NCg0KIVtdKENsZWF2ZWxhbmQtVmlzdWFsaXppbmcuanBnKQ0KDQpUaGUgZ2dwbG90MiBwYWNrYWdlIGNvbnRhaW5zIHRoZSBgZmFjZXRfZ3JpZGAgZnVuY3Rpb24gd2hpY2ggaXMgdXNlZCB0byBkZWZpbmUgdGhlIGdyaWQgb24gd2hpY2ggdGhlIHN1Yi1wbG90cyBhcmUgY3JlYXRlZC4gVGhlIGZhY2V0IGdyaWQgZnVuY3Rpb24gdXNlcyBhbiBSIGZvcm11bGEgb2JqZWN0IHRvIGRlZmluZSB0aGUgcm93cyBhbmQgY29sdW1ucyB0byBzcGVjaWZ5IHRoZSBjb25kaXRpb25pbmcgdmFyaWFibGUgdXNlZCB0byBkZWZpbmUgdGhlIHJvd3MgYW5kIGNvbHVtbnMuIFRoZSBnZW5lcmFsIGZvcm0gb2YgdGhpcyBmb3JtdWxhIGlzOg0KDQokJFJvd1ZhcmlhYmxlcyBcc2ltIENvbHVtblZhcmlhYmxlcyQkDQoNCkEgY29uZGl0aW9uZWQgcGxvdCB3aXRoIGEgc2luZ2xlIGNvbHVtbiwgYnV0IG11bHRpcGxlIHJvd3MsIGlzIHRoZXJlZm9yZSBkZWZpbmVkOg0KDQokJFJvd1ZhcmlhYmxlcyBcc2ltXCAuJCQNCg0KT3IsIGNvbmRpdGlvbmVkIHBsb3Qgd2l0aCBhIHNpbmdsZSByb3csIGJ1dCBtdWx0aXBsZSBjb2x1bW5zLCBpcyBkZWZpbmVkOg0KDQokJC5cIFxzaW0gQ29sdW1uVmFyaWFibGVzJCQNCg0KWW91IGNhbiB1c2UgbXVsdGlwbGUgdmFyaWFibGVzIHRvIGNvbmRpdGlvbiByb3dzIGFuZCBjb2x1bW5zLCB1c2luZyB0aGUgJCskIHN5bWJvbCBhcyB0aGUgb3BlcmF0b3I6DQoNCiQkUm93VmFyMSArIFJvd1ZhcjIgKyBcbGRvdHMgXHNpbSBDb2xWYXIxICsgQ29sVmFyMiArIFxsZG90cyQkDQoNCkxpa2UgYWxsIGdvb2QgdGhpbmdzIGluIHZpc3VsYXRpb24sIHRoZXJlIGFyZSBwcmFjdGljYWwgbGltaXRzLiBDcmVhdGluZyBhIGxhcmdlIGdyaWQgb2Ygc3ViLXBsb3RzIHVzaW5nIG11bHRpcGxlIGNvbmRpdGlvbmluZyB2YXJpYWJsZXMgcXVpY2tseSBiZWNvbWVzIGNvbmZ1c3NpbmcgdG8gbG9vayBhdC4gDQoNClRoZSBjb2RlIGluIHRoZSBjZWxsIGJlbG93IGNyZWF0ZXMgYSBmYWNldGVkIHBsb3Qgb2YgbW9udGhseSBhdmVyYWdlIGZsaWdodCBkZWxheSBieSBtb250aC4gVGhlIGVhY2ggZWFjaCBvZiB0aGUgcGxvdHMgaXMgZ3JvdXBlZC1ieSBvciBjb25kaXRpb25lZCBvbiBmaXJzdCB0aGUgbmFtZSBvZiB0aGUgYWlybGluZSBhbmQgdGhlbiB0aGUgbWVhbiBmbGlnaHQgZGVsYXkuICANCg0KDQpgYGB7cn0NCmdncGxvdChjcl9tbl9hcnJfZGVsYXksIGFlcyhtb250aCwgbWVhbl9kZWxheSkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9saW5lKCkgKw0KICBmYWNldF9ncmlkKH4gZmN0X3Jlb3JkZXIobmFtZSwgbWVhbl9kZWxheSkpDQpgYGANCg0KVGhlcmUgaXMgb25lIHBsb3QgZm9yIGVhY2ggYWlybGluZSwgd2l0aCB0aGUgbWVhbiBkZWxheSBzaG93biBieSBtb250aC4gVGhlc2UgcGxvdHMgaGF2ZSBiZWVuIHNvcnRlZCBieSB0aGUgbWVhbiBkZWxheSBieSBhaXJsaW5lLCBzbyB3ZSBjYW4gZm9jdXMgb24gdGhlIGFpcmxpbmVzIHdpdGggdGhlIGdyZWF0ZXN0IGF2ZXJhZ2UgZGVsYXlzLiBUaGVyZSBpcyBzaWduaWZpY2FudCBjaGFuZ2VzIGluIHRoZSBtZWFuIGRlYWx5cyBieSBtb250aCBmb3IgZWFjaCBhaXJsaW5lLg0KDQpOZXh0LCBsZXQncyBsb29rIGF0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgYWlyaW5lcyBhbmQgdGhlIG51bWJlciBvZiBmbGlnaHRzLiBUaGUgY29kZSBiZWxvdyBjcmVhdGVzIGEgZGlzcGxheSBvZiB0aGUgbnVtYmVyIG9mIGZsaWdodHMgcGVyIG1vbnRoIGJ5IGFpcmxpbmUgc29ydGVkIGJ5IG1lYW4gZmxpZ2h0IGRlbGF5LiBUaGUgbnVtYmVyIG9mIGZsaWdodHMgaW4gYSBnaXZlbiBtb250aCBpcyBkaXNwbGF5ZWQgb24gYSBsb2cgc2NhbGUuIA0KDQpgYGB7Un0NCiMgbG9vayBhdCBudW1iZXIgb2YgZmxpZ2h0cw0KZ2dwbG90KGNyX21uX2Fycl9kZWxheSwgYWVzKG1vbnRoLCBuKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX2xpbmUoKSArDQogIHNjYWxlX3lfbG9nMTAoKSArDQogIGZhY2V0X2dyaWQofiBmY3RfcmVvcmRlcihuYW1lLCBtZWFuX2RlbGF5KSkNCmBgYA0KDQpPbmNlIGFnYWlubiwgdGhlcmUgaXMgbm8gY2xlYXIgcGF0dGVybiBiZXR3ZWVuIHRoZSBudW1iZXIgb2YgZmxpZ2h0cyBhbmQgdGhlIG1lYW4gZGVsYXlzLiBGdXRoZXIsIGZvciBlYWNoIGFpcmxpbmUgdGhlcmUgaXMgb25seSBtaW5pbWFsIGNoYW5nZXMgaW4gdGhlIG51bWJlciBvZiBmbGlnaHRzIGJ5IG1vbnRoLg0KDQpMZXRzIGxvb2sgYXQgdGhlIHNhbWUgbG90IG9uIGEgbGluZWFyIHNjYWxlLiANCg0KYGBge1J9DQpnZ3Bsb3QoY3JfbW5fYXJyX2RlbGF5LCBhZXMobW9udGgsIG4pKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fbGluZSgpICsNCiAgZmFjZXRfZ3JpZCh+IGZjdF9yZW9yZGVyKG5hbWUsIG1lYW5fZGVsYXkpKQ0KYGBgDQoNClRoZSBjb25jbHVzaW9ucyB3ZSBjYW4gZHJhdyBmcm9tIHRoaXMgY2hhcnQgYXJlIHRoZSBzYW1lIGFzIGJlZm9yZS4gDQoNCiMjIEZpbHRlcmluZyBSZXN1bHRzIHRvIEltcHJvdmUgVW5kZXJzdGFuZGluZw0KDQpUbyB1bmRlcnN0YW5kIHRoZSBtb250aCB0byBtb250aCBjaGFuZ2VzIGluIG1lYW4gZmxpZ2h0IGRlbGF5IGZvciBhaXJsaW5lcyB3ZSB3aWxsIHBsb3QgdGhlc2UgZGVsYXlzIGJ5IG1vbnRoLiBUbyBkbyBzbyB3ZSBuZWVkIHRvIGZpbHRlciB0aGUgbnVtYmVyIG9mIGFpcmxpbmVzIHRvIGp1c3QgdGhlIGZldyB3aXRoIHRoZSBsYXJnZXN0IG51bWJlciBvZiBmbGlnaHRzLiBUaGUgY29kZSBpbiB0aGUgY2VsbCBiZWxvdyBkb2VzIHRoZSAgZm9sbG93aW5nOg0KDQotIEZpbmQgdGhlIGFpcmxpbmUgd2l0aCB0aGUgbGFyZ2VzdCBudW1iZXIgb2YgZmxpZ2h0cywgYW5kIGNvbnZlcnQgdGhlIGFpcmxpbmUgY29kZXMgdG8gY2hhcmFjdGVyLg0KLSBUaGUgcGlwZWxpbmUgZm9yIHBsb3R0aW5nIHRoZSBtb250aGx5IGZsaWdodCBkZWxheXMgZG9lcyB0aGUgZm9sbG93aW5nOiANCiAgLSBUaGUgYWlybGluZXMgYXJlIGZpbHRlcmVkIGZvciB0aGUgb25lcyB3aXRoIHRoZSBsYXJnZSBudW1iZXIgb2YgZmxpZ2h0cy4NCiAgLSBBIHBsb3QgaXMgY3JlYXRlZCBvZiB0aGUgbWVhbiBmbGlnaHQgZGVsYXkgYnkgbW9udGggZm9yIHRoZSBhaXJsaW5lcyB3aXRoIHRoZSBsYXJnZXN0IG51bWJlcnMgb2YgZmxpZ2h0cy4gDQoNCmBgYHtSfQ0KdG9wNyA9IGNyX2Fycl9kZWxheSAlPiUgZmlsdGVyKG4gPiAzODAwMDApICU+JSBzZWxlY3QoY2FycmllcikNCnRvcDcgPSBzYXBwbHkodG9wNywgYXMuY2hhcmFjdGVyKQ0KdG9wNyANCg0KIyBvdmVybGF5IHRoZW0gYWxsDQpjcl9tbl9hcnJfZGVsYXkgJT4lDQogIGZpbHRlcihjYXJyaWVyICVpbiUgdG9wNykgJT4lDQogIGdncGxvdChhZXMobW9udGgsIG1lYW5fZGVsYXksIGNvbG9yID0gbmFtZSkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9saW5lKCkNCmBgYA0KDQpUaGVyZSBpcyBjbGVhcmx5IGEgc2Vhc29uYWwgcGF0ZXJuIHRvIHRoZSBtZWFuIGRlbGF5cywgd2hpY2ggaXMgc2ltaWxhciBmb3IgZWFjaCBhaXJsaW5lLiANCg0KIyMgRnVydGhlciAgRHJpbGwgRG93bg0KDQpHaXZlbiB0aGlzIG1vbnRobHkgcGF0dGVybiwgaXQgd2lsbCBiZSBpbnRlcmVzdGluZyB0byBkcmlsbCBkb3duIGludG8gbW9yZSBkZXRhaWwuIA0KcXVlc3Rpb25zOg0KLSBBcmUgZGlmZmVyZW50IGRlc3RpbmF0aW9ucyBtb3JlIHByb25lIHRvIGRlbGF5cz8NCi0gZG9lcyB2YXJpYWJpbGl0eSBhY3Jvc3MgYWlybGluZXMgY2hhbmdlIGZvciBkaWZmZXJlbnQgZGVzdGluYXRpb25zPw0KDQpsZXQncyBsb29rIGludG8gdGhlc2UgYnkgZ3JvdXBpbmcgYnkgZGVzdCwgbW9udGgsIGFuZCBuYW1lIHdlJ2xsIGxvb2sgYXQgbWVhbiBkZWxheSBmb3IgdGhvc2UgYWlybGluZXMgd2l0aCBlbm91Z2ggb2JzZXJ2YXRpb25zLiBJbiB0aGlzIGNhc2Ugd2UgbmVlZCB0byBjcmVhdGUgYSBuZXcgZ3JvdXBpbmcgb2YgdGhlIGxhcmdlIGRhdGEgc2V0IHVzaW5nIFNwYXJseXIuIFRoZSBzcGFya2x5ciBwaXBlbGluZSBpbiB0aGUgY2VsbCBiZWxvdyBwZXJmb3JtcyB0aGUgZm9sbG93aW5nIG9wZXJ0aW9uczoNCg0KLSBHcm91cHMgdGhlIGRhdGEgZmlyc3QgYnkgdGhlIGZsaWdodCBvcmlnaW4sIHRoZW4gYnkgdGhlIGZsaWdodCBkZXN0aW5hdGlpb24gYW5kIGZpbmFsbHkgYnkgbW9udGguDQotIFRoZSBtZWFuIGRlbGF5IGFuZCBudW1iZXIgb2YgZmxpZ2h0cyBvbiBlYWNoIHJvdXRlIGFyZSBjb21wdXRlZC4gDQotIFJlc3VsdHMgd2l0aCBmZXdlciB0aGFuIDUwIGZsaWdodHMgcGVyIG1vbnRoIGFyZSBmaWx0ZXJlZCBvdXQuDQotIFRoZSByZXN1bHRzIGFyZSBjb2xsZWN0ZWQgYmFjayBpbnRvIHlvdXIgbG9jYWwgUiBzZXNzaW9uLiANCg0KDQpgYGB7Un0NCiMgZ3JvdXAgYnksIG9yaWdpbiwgZGVzdCwgY2FycmllciwgbW9udGggYW5kIGdldCBtZWFuIGRlbGF5IGFuZCAjIG9icw0KIyBhbmQgcHVsbCB0aGlzIGJhY2sgaW50byBSDQpyb3V0ZV9zdW1tID0gZmxpZ2h0c190YmwgJT4lDQogIGdyb3VwX2J5KG9yaWdpbiwgZGVzdCwgY2FycmllciwgbW9udGgpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbWVhbl9kZWxheSA9IG1lYW4oYXJyX2RlbGF5KSwNCiAgICBuID0gbigpKSAlPiUNCiAgZmlsdGVyKG4gPj0gNTApICU+JQ0KICBjb2xsZWN0KCkNCg0KbnJvdyhyb3V0ZV9zdW1tKQ0KYGBgDQoNCldlIGhhdmUgZ29uZSBmcm9tIG92ZXIgMiBtaWxsaW9uIHJvd3MgdG8gYWJvdXQgNTAgdGhvdXNhbmQgcm93cywgYSByZWR1Y3Rpb24gaW4gc2lkZSBvZiBhYm91dCBhIGZhY3RvciBvZiBhYm91dCA0MC4gV2l0aCBzb21lIGZ1cnRoZXIgZmlsdGVyaW5nLCB3ZSBjYW4gcmVkdWNlIHRoaXMgIGV2ZW50IGZ1cnRoZXIuIFRoaXMgdHlwZSBvZiByZWR1Y3Rpb24gaW4gZGF0YSBzaXplIGlzIGVzc2VudGlhbCB3aGVuIGFwcGx5aW5nIHRoZSBEJlIgcGFyYWRpZ20gdG8gbGFyZ2UgY29tcGxleCBkYXRhIHNldHMuIA0KDQpMZXQncyBkbyBzb21lICBhZGRpdGlvbmFsIGZpbHRlcmluZyB0byBwcmVwYXJlIHRoZXNlIGRhdGEgZm9yIHZpc3VhbGl6YXRpb24uIE91ciBnb2FsIGlzIHRvIHJlZHVjZSB0aGUgc2l6ZSBvZiB0aGUgZGF0YSBzZXQgYW4gZmlsdGVyIGZvciB0aGUgY2FycmllcnMgd2l0aCB0aGUgd2l0aCB0aGUgbW9zdCBmbGlnaHRzLiANCg0KLSBXZSBmaWx0ZXIgZm9yIHRoZSA3IGFpcmxpbmVzIHdpdGggdGhlICBsYXJnZXN0IG51bWJlcnMgb2YgZmxpZ2h0cy4gDQotIFRoZSBhaXJsaW5lIG5hbWVzIGFyZSBqb2luZWQuDQotIFRoZSBhaXJsaW5lIG5hbWVzIGFyZSAgY29udmVydGVkIHRvIGEgZmFjdG9yIHZhcmlhYmxlIHdoaWNoIGlzIGhlbHBmdWwgZm9yIG1ha2luZyBwbG90cyB3aXRoIGdncGxvdCAyLiANCg0KDQpgYGB7Un0NCnJvdXRlX3N1bW03IDwtIA0KICBmaWx0ZXIocm91dGVfc3VtbSwgY2FycmllciAlaW4lIHRvcDcpICU+JQ0KICBsZWZ0X2pvaW4oYWlybGluZXMpICU+JQ0KICByZW5hbWUoY2Fycmllcl9uYW1lID0gbmFtZSkgJT4lDQogIG11dGF0ZShjYXJyaWVyX25hbWUgPSBmYWN0b3IoY2Fycmllcl9uYW1lKSkNCg0KIyBub3cgbGV0J3MgbmVzdCB0aGUgZGF0YSBieSBvcmlnaW4gYW5kIGRlc3QgKG5lZWQgdG8gZXhwbGFpbiB0aGlzLi4uKQ0KYnlfcm91dGUgPC0gcm91dGVfc3VtbTcgJT4lDQogIGdyb3VwX2J5KG9yaWdpbiwgZGVzdCkgJT4lDQogIG5lc3QoKQ0KICANCmJ5X3JvdXRlDQoNCmBgYA0KDQoNCldlIGFyZSBsZWZ0IHdpdGggYWJvdXQgMiwyMDAgcm91dGVzLiAgVGhlIGRhdGEgZm9yIGVhY2ggcm91dGUgaXMgc3RvcmVkIGFzIGEgZGF0YSBmcmFtZSBpbiB0aGUgJ2RhdGEnIGNvbHVtbi4gDQoNCioqKg0KKipOb3RlOioqIElmIHdlIGhhZCBhIGxhcmdlIGRhdGEgc2V0IHdlIGNvdWxkIGVhc2lseSBwZXJmb3JtIHRoZXNlIHRyYW5zZm9ybWF0aW9ucyBpbiBvdXIgc2NhbGFibGUgYmFjay1lbmQuDQoqKioNCg0KQXMgYSBuZXh0IHN0ZXAgd2UgZmlsdGVyIHNvbWUgcm91dGVzIHdpdGggc3BhcnNlIGRhdGEuIFNwZWNpZmljYWxseSwgd2Ugd2lsbCBmaWx0ZXIgZm9yIHJvdXRlcyB0aGF0IGhhdmUgZGF0YSBmb3IgZXZlcnkgbW9udGggb2YgdGhlIHllYXJzLiBUaGVzZSBzdGVwcyBhcmUgc2ltcGxlLg0KDQotIFdlIGNvdW50IHRoZSB1bmlxdWUgbnVtYmVyIG9mIG1vbnRocy4NCi0gV2UgY2FsY3VsYXRlIHRoZSB1bmlxdWUgbnVtYmVyIG9mIGNhcnJpZXJzIHdoaWxlIHdlJ3JlIGF0IGl0Lg0KLSBGaWx0ZXIgb24gdGhlIGNvdW50IG9mICB1bmlxdWUgbW9udGhzLiANCg0KDQpgYGB7Un0NCmJ5X3JvdXRlIDwtIGJ5X3JvdXRlICU+JQ0KICBtdXRhdGUoDQogICAgbl9tb250aHMgPSBtYXBfaW50KGRhdGEsIH4gbl9kaXN0aW5jdCguJG1vbnRoKSksDQogICAgbl9jYXJyaWVycyA9IG1hcF9pbnQoZGF0YSwgfiBuX2Rpc3RpbmN0KC4kY2FycmllcikpDQogICAgIyBtaW55ID0gbWFwX2RibChkYXRhLCB+IG1pbiguJG1lYW5fZGVsYXksIG5hLnJtID0gVFJVRSkpLA0KICAgICMgbWF4eSA9IG1hcF9kYmwoZGF0YSwgfiBtYXgoLiRtZWFuX2RlbGF5LCBuYS5ybSA9IFRSVUUpKQ0KICApDQoNCmJ5X3JvdXRlDQpgYGANCg0KVGhpcyByZXN1bHQgaXMgYXMgZXhwZWN0ZWQuIE5vdyBpdCBpcyB0aW1lIHRvIGZpbHRlciBmb3Igcm91dGVzIHdpdGggMTIgbW9udGhzIG9mIGRhdGEuIA0KDQoNCmBgYHtSfQ0KYnlfcm91dGUgPC0gZmlsdGVyKGJ5X3JvdXRlLCBuX21vbnRocyA9PSAxMikgJT4lDQogIHNlbGVjdCgtbl9tb250aHMpDQoNCmJ5X3JvdXRlDQpgYGANCg0KVGhlcmUgYXJlIG9ubHkgMSw3MDAgcm91dGVzIHdpdGggMTIgbW9udGhzIG9mIGRhdGEuIA0KDQpXZSBjYW4gY3JlYXRlIGEgc2ltcGxlIHN1bW1hcnkgb2YgdGhlc2UgZGF0YSBieSBjb21wdXRpbmcgdGhlIG1lYW4gZGVsYXkgYnkgbW9udGgNCg0KYGBge1J9DQojIGxldCdzIHN1bW1hcml6ZSBqdXN0IGJ5IG1vbnRoDQptbl9hcnJfZGVsYXkgPSBmbGlnaHRzX3RibCAlPiUNCiAgZ3JvdXBfYnkobW9udGgpICU+JQ0KICBzdW1tYXJpc2UobWVhbl9kZWxheSA9IG1lYW4oYXJyX2RlbGF5KSkgJT4lDQogIGFycmFuZ2UobW9udGgpICU+JQ0KICBjb2xsZWN0KCkNCg0KbW5fYXJyX2RlbGF5DQpgYGANCg0KV2UgaGF2ZSByZWR1Y2VkIG91ciAyIG1pbGxpb24gcm93cyB0byBqdXN0IDEwLiANCg0KDQojIyBQbG90aW5nIENvbXBsZXggRGF0YSB3aXRoIFRyZWxsaXNjb3BlLiANCg0KV2UgaGF2ZSBhIGNvbXBsZXggZGF0YSBzZXQgd2l0aCBpbmZvcm1hdGlvbiBvbiBmbGlnaHQgZGVsYXlzIGZvciAxLDcwMCByb3V0ZXMuIEhvdyBjYW4gd2UgdW5kZXJzdGFuZCB0aGVzZSBjb21wbGV4IGRhdGE/IA0KDQpXZSBoYXZlIGFscmVhZHkgbG9va2VkIGF0IHVzaW5nIHRoZSBtZXRob2Qgb2Ygc21hbGwgZnJhY3Rpb25zIG9yIGNvbmRpdGlvbmluZyB0byB1bmRlcnN0YW5kIGNvbXBsZXggZGF0YS4gRm9yIHRoaXMgZXhhbXBsZSwgd2Ugd2lsbCB1c2UgYSB0b29sIGNhbGxlZCAqKlRyZWxpc2NvcGUqKiB3aGljaCBhbGxvd3MgaW50ZXJhY3RpdmUgdmlzdWFsaXphdGlvbiBvZiBjb21wbGV4IGRhdGEgc2V0cyB1c2luZyB0aGUgbWV0aG9kIG9mIHNtYWxsIG11bHRpcGxlcy4gDQoNCmBgYHtSfQ0KIyBsZXQncyBtYWtlIGEgcGxvdCBjb2x1bW4gZm9yIGVhY2ggcm91dGUNCmJ5X3JvdXRlIDwtIGJ5X3JvdXRlICU+JQ0KICBtdXRhdGUoDQogICAgcGxvdCA9IG1hcF9wbG90KGRhdGEsIGZ1bmN0aW9uKHgpIHsNCiAgICAgIGdncGxvdCh4LCBhZXMobW9udGgsIG1lYW5fZGVsYXksIGNvbG9yID0gY2Fycmllcl9uYW1lKSkgKw0KICAgICAgICBnZW9tX2xpbmUoYWVzKG1vbnRoLCBtZWFuX2RlbGF5KSwgZGF0YSA9IG1uX2Fycl9kZWxheSwNCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gImdyYXkiLCBzaXplID0gMSkgKw0KICAgICAgICBnZW9tX3BvaW50KCkgKyBnZW9tX2xpbmUoKSArDQogICAgICAgIHlsaW0oYygtMzMuNSwgOTYuMjUpKSArDQogICAgICAgIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKGRyb3AgPSBGQUxTRSkNCiAgICB9KQ0KICApDQoNCmJ5X3JvdXRlDQpgYGANCg0KDQoNCg0KDQpgYGB7Un0NCnRyZWxsaXNjb3BlKGJ5X3JvdXRlLCBuYW1lID0gInRlc3QiLCBucm93ID0gMiwgbmNvbCA9IDQsIHNlbGZfY29udGFpbmVkID0gVFJVRSkNCg0KI2Jyb3dzZVVSTChwYXN0ZTAoYXR0cihwLCAidHJlbGxpc2NvcGVfcGFycyIpJHd3d19kaXIsICIvaW5kZXguaHRtbCIpKQ0KYGBgDQoNCg==